function _contains(nodeA, nodeB) {
  // Bypass comparison on document node since
  // we don't deal with cross frame,
  // e.g. document.body.parentNode.parentNode == document (false for IE).
  if (nodeA.nodeType == 9 && nodeB.nodeType != 9) {
    return true;
  }
  while ((nodeB = nodeB.parentNode)) {
    if (nodeB == nodeA) {
      return true;
    }
  }
  return false;
}

var _getSetAttrDiv = document.createElement("div");
_getSetAttrDiv.setAttribute("className", "t");
var _GET_SET_ATTRIBUTE = _getSetAttrDiv.className !== "t";

function _getAttr(el, key) {
  key = key.toLowerCase();
  var val = null;
  // IE6,IE7,IE=EmulateIE7
  if (!_GET_SET_ATTRIBUTE && el.nodeName.toLowerCase() != "script") {
    var div = el.ownerDocument.createElement("div");
    div.appendChild(el.cloneNode(false));
    var list = _getAttrList(_unescape(div.innerHTML));
    if (key in list) {
      val = list[key];
    }
  } else {
    // IE8����<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />���׳��쳣
    try {
      val = el.getAttribute(key, 2);
    } catch (e) {
      val = el.getAttribute(key, 1);
    }
  }
  if (key === "style" && val !== null) {
    val = _formatCss(val);
  }
  return val;
}

function _queryAll(expr, root) {
  var exprList = expr.split(",");
  if (exprList.length > 1) {
    var mergedResults = [];
    _each(exprList, function() {
      _each(_queryAll(this, root), function() {
        if (_inArray(this, mergedResults) < 0) {
          mergedResults.push(this);
        }
      });
    });
    return mergedResults;
  }
  root = root || document;
  function escape(str) {
    if (typeof str != "string") {
      return str;
    }
    return str.replace(/([^\w\-])/g, "\\$1");
  }
  function stripslashes(str) {
    return str.replace(/\\/g, "");
  }
  function cmpTag(tagA, tagB) {
    return tagA === "*" || tagA.toLowerCase() === escape(tagB.toLowerCase());
  }
  function byId(id, tag, root) {
    var arr = [],
      doc = root.ownerDocument || root,
      el = doc.getElementById(stripslashes(id));
    if (el) {
      if (cmpTag(tag, el.nodeName) && _contains(root, el)) {
        arr.push(el);
      }
    }
    return arr;
  }
  function byClass(className, tag, root) {
    var doc = root.ownerDocument || root,
      arr = [],
      els,
      i,
      len,
      el;
    if (root.getElementsByClassName) {
      els = root.getElementsByClassName(stripslashes(className));
      for (i = 0, len = els.length; i < len; i++) {
        el = els[i];
        if (cmpTag(tag, el.nodeName)) {
          arr.push(el);
        }
      }
    } else if (doc.querySelectorAll) {
      els = doc.querySelectorAll(
        (root.nodeName !== "#document" ? root.nodeName + " " : "") +
          tag +
          "." +
          className
      );
      for (i = 0, len = els.length; i < len; i++) {
        el = els[i];
        if (_contains(root, el)) {
          arr.push(el);
        }
      }
    } else {
      els = root.getElementsByTagName(tag);
      className = " " + className + " ";
      for (i = 0, len = els.length; i < len; i++) {
        el = els[i];
        if (el.nodeType == 1) {
          var cls = el.className;
          if (cls && (" " + cls + " ").indexOf(className) > -1) {
            arr.push(el);
          }
        }
      }
    }
    return arr;
  }
  function byName(name, tag, root) {
    var arr = [],
      doc = root.ownerDocument || root,
      els = doc.getElementsByName(stripslashes(name)),
      el;
    for (var i = 0, len = els.length; i < len; i++) {
      el = els[i];
      if (cmpTag(tag, el.nodeName) && _contains(root, el)) {
        if (el.getAttribute("name") !== null) {
          arr.push(el);
        }
      }
    }
    return arr;
  }
  function byAttr(key, val, tag, root) {
    var arr = [],
      els = root.getElementsByTagName(tag),
      el;
    for (var i = 0, len = els.length; i < len; i++) {
      el = els[i];
      if (el.nodeType == 1) {
        if (val === null) {
          if (_getAttr(el, key) !== null) {
            arr.push(el);
          }
        } else {
          if (val === escape(_getAttr(el, key))) {
            arr.push(el);
          }
        }
      }
    }
    return arr;
  }
  function select(expr, root) {
    var arr = [],
      matches;
    matches = /^((?:\\.|[^.#\s\[<>])+)/.exec(expr);
    var tag = matches ? matches[1] : "*";
    if ((matches = /#((?:[\w\-]|\\.)+)$/.exec(expr))) {
      arr = byId(matches[1], tag, root);
    } else if ((matches = /\.((?:[\w\-]|\\.)+)$/.exec(expr))) {
      arr = byClass(matches[1], tag, root);
    } else if ((matches = /\[((?:[\w\-]|\\.)+)\]/.exec(expr))) {
      arr = byAttr(matches[1].toLowerCase(), null, tag, root);
    } else if (
      (matches = /\[((?:[\w\-]|\\.)+)\s*=\s*['"]?((?:\\.|[^'"]+)+)['"]?\]/.exec(
        expr
      ))
    ) {
      var key = matches[1].toLowerCase(),
        val = matches[2];
      if (key === "id") {
        arr = byId(val, tag, root);
      } else if (key === "class") {
        arr = byClass(val, tag, root);
      } else if (key === "name") {
        arr = byName(val, tag, root);
      } else {
        arr = byAttr(key, val, tag, root);
      }
    } else {
      var els = root.getElementsByTagName(tag),
        el;
      for (var i = 0, len = els.length; i < len; i++) {
        el = els[i];
        if (el.nodeType == 1) {
          arr.push(el);
        }
      }
    }
    return arr;
  }
  var parts = [],
    arr,
    re = /((?:\\.|[^\s>])+|[\s>])/g;
  while ((arr = re.exec(expr))) {
    if (arr[1] !== " ") {
      parts.push(arr[1]);
    }
  }
  var results = [];
  if (parts.length == 1) {
    return select(parts[0], root);
  }
  var isChild = false,
    part,
    els,
    subResults,
    val,
    v,
    i,
    j,
    k,
    length,
    len,
    l;
  for (i = 0, lenth = parts.length; i < lenth; i++) {
    part = parts[i];
    if (part === ">") {
      isChild = true;
      continue;
    }
    if (i > 0) {
      els = [];
      for (j = 0, len = results.length; j < len; j++) {
        val = results[j];
        subResults = select(part, val);
        for (k = 0, l = subResults.length; k < l; k++) {
          v = subResults[k];
          if (isChild) {
            if (val === v.parentNode) {
              els.push(v);
            }
          } else {
            els.push(v);
          }
        }
      }
      results = els;
    } else {
      results = select(part, root);
    }
    if (results.length === 0) {
      return [];
    }
  }
  return results;
}

function _query(expr, root) {
  var arr = _queryAll(expr, root);
  return arr.length > 0 ? arr[0] : null;
}

K.query = _query;
K.queryAll = _queryAll;
